package main.leetcode.clockin.July;

import java.util.Arrays;

/**
 * 174. 地下城游戏
 *
 * <p>一些恶魔抓住了公主（P）并将她关在了地下城的右下角。地下城是由 M x N 个房间组成的二维网格。
 * 我们英勇的骑士（K）最初被安置在左上角的房间里，他必须穿过地下城并通过对抗恶魔来拯救公主。
 *
 * <p>骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下，他会立即死亡。
 *
 * <p>有些房间由恶魔守卫，因此骑士在进入这些房间时会失去健康点数 （若房间里的值为负整数，则表示骑士将损失健康点数）；其他房间要么是空的（房间里的值为0），
 * 要么包含增加骑士健康点数的魔法球（若房间里的值为正整数，则表示骑士将增加健康点数）。
 *
 * <p>为了尽快到达公主，骑士决定每次只向右或向下移动一步。
 *
 * <p>编写一个函数来计算确保骑士能够拯救到公主所需的最低初始健康点数。
 *
 * <p>例如，考虑到如下布局的地下城，如果骑士遵循最佳路径 右 -> 右 -> 下 -> 下，则骑士的初始健康点数至少为 7。
 *
 * <p>-2 (K) -3 3 <br>
 * -5 -10 1 <br>
 * 10 30 -5 (P)
 *
 * <p>说明:<br>
 * 骑士的健康点数没有上限。<br>
 * 任何房间都可能对骑士的健康点数造成威胁，也可能增加骑士的健康点数，包括骑士进入的左上角房间以及公主被监禁的右下角房间。
 */
public class day12 {
    private int res;

    public static void main(String[] args) {
        System.out.println(
                new day12()
                        .calculateMinimumHP(new int[][] {{-2, -3, 3}, {-5, -10, 1}, {10, 30, -5}}));
        System.out.println(new day12().calculateMinimumHP(new int[][] {{0}}));
        System.out.println(new day12().calculateMinimumHP(new int[][] {{100}}));
    }

    /** 回溯 + 记忆化存储 */
    public int calculateMinimumHP1(int[][] dungeon) {
        if (dungeon == null || dungeon.length == 0) {
            return Integer.MAX_VALUE;
        }
        int m = dungeon.length;
        int n = dungeon[0].length;
        int[][] memo = new int[m][n];
        for (int[] ints : memo) {
            Arrays.fill(ints, -1);
        }
        return backTrack(dungeon, 0, 0, m, n, memo) + 1;
    }

    private int backTrack(int[][] dungeon, int i, int j, int m, int n, int[][] memo) {
        if (i > m - 1 || j > n - 1) {
            return Integer.MAX_VALUE;
        }
        if (memo[i][j] != -1) {
            return memo[i][j];
        }
        if (i == m - 1 && j == n - 1) {
            return dungeon[i][j] >= 0 ? 0 : -dungeon[i][j];
        }
        // 右边需要的最小值
        int down = backTrack(dungeon, i + 1, j, m, n, memo);
        // 下边需要的最小值
        int right = backTrack(dungeon, i, j + 1, m, n, memo);
        // 当前格子所需要的最小值
        int min = Math.min(down, right) - dungeon[i][j];
        // 如果需要的小于0，则返回0
        return Math.max(min, 0);
    }

    public int calculateMinimumHP(int[][] dungeon) {
        if (dungeon == null || dungeon.length == 0 || dungeon[0].length == 0) {
            return Integer.MAX_VALUE;
        }
        int m = dungeon.length;
        int n = dungeon[0].length;
        int[][] dp = new int[m + 1][n + 1];
        for (int i = 0; i < m; ++i) {
            dp[i][n] = Integer.MAX_VALUE;
        }
        for (int i = 0; i < n; ++i) {
            dp[m][i] = Integer.MAX_VALUE;
        }
        dp[m][n - 1] = dp[m - 1][n] = 1;
        for (int i = m - 1; i >= 0; --i) {
            for (int j = n - 1; j >= 0; --j) {
                dp[i][j] = Math.max(Math.min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j], 1);
            }
        }
        return dp[0][0];
    }
}
